package net.lcqf.tcp.game.room;

import net.lcqf.tcp.game.match.model.MatchPlayer;
import net.lcqf.tcp.utils.LocalCacheUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

/**
 * +----------------------------------------------------------------------
 * // | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * // +----------------------------------------------------------------------
 * // | Copyright (c) 2021-2025 聊来 All rights reserved.
 * // +----------------------------------------------------------------------
 * // | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * // +----------------------------------------------------------------------
 * // | @Author: 聊来 <18970881148@qq.com>
 * // +----------------------------------------------------------------------
 * // | DateTime: 2025/3/12 16:52
 * // +----------------------------------------------------------------------
 */
public class RoomManager {

    private static final Logger logger = LoggerFactory.getLogger(RoomManager.class);

    private final Integer roomId;
    public final List<MatchPlayer> matchPlayerList;

    public final ConcurrentHashMap<String, MatchPlayer> players = new ConcurrentHashMap<>();

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, r -> new Thread(r, "room-" + LocalCacheUtil.ROOM_EXECUTOR.getAndIncrement()));
    private final AtomicBoolean isReadyFinalized = new AtomicBoolean(false);

    private static final int CONFIRM_TIMEOUT = 30;

    private final ReentrantLock lock = new ReentrantLock();

    public RoomManager(Integer roomId, List<MatchPlayer> matchPlayerList) {
        this.roomId = roomId;
        this.matchPlayerList = matchPlayerList;
        for (MatchPlayer player : matchPlayerList) {
            players.put(player.getChannelId(), player);
        }
        startConfirmationTimer();
    }

    /**
     * 玩家确认
     * @param channelId 玩家标识
     * @return {@code boolean}
     */
    public boolean confirmReady(String channelId) {
        if (isReadyFinalized.get()) { return false; }

        MatchPlayer player = players.get(channelId);
        if (player != null) {
            player.setReady(true);
            checkAllReady();
            return true;
        }

        return false;
    }

    /**
     * 玩家取消
     * @param channelId 玩家标识
     * @return {@code boolean}
     */
    public boolean cancelReady(String channelId) {
        if (isReadyFinalized.get()) { return false; }

        MatchPlayer player = players.get(channelId);

        if (player != null) {
            player.setReady(false);
            finalizeMatch(false);
            return true;
        }

        return false;
    }

    /**
     * 检查玩家是否全部准备
     */
    private void checkAllReady() {
        final ReentrantLock lock = this.lock;
        // 第一个检查，无锁快速判断
        if (!isReadyFinalized.get()) {
            lock.lock();
            try {
                // 第二次检查，持有锁后再次确认状态
                if (!isReadyFinalized.get()) {
                    boolean allReady = players.values().stream().allMatch(MatchPlayer::isReady);
                    if (allReady) {
                        finalizeMatch(true);
                    }
                }
            } catch (Exception e) {
                logger.info("检查是否全部准备出现异常：" + e.getMessage());
            }finally {
                lock.unlock();
            }
        }

    }

    /**
     * TODO 后续将房间以及玩家信息存入redis,游戏结束后为结算做准备
     * 完成匹配
     * @param result 结果
     */
    private void finalizeMatch(boolean result) {
        if (isReadyFinalized.compareAndSet(false, true)) {
            scheduler.shutdown();
            if (result) {
                logger.info("房间" + roomId + "匹配成功，游戏开始");
            } else {
                logger.info("有玩家退出");
            }
        }
    }

    /**
     * 开启确认超时定时器
     */
    private void startConfirmationTimer() {
        scheduler.schedule(() -> {
            if (!isReadyFinalized.getAndSet(true)) {
                logger.info(players.size() + "名玩家没有全部点击确认，房间" + roomId + "关闭");
                clearAll();
            }
        }, CONFIRM_TIMEOUT, TimeUnit.SECONDS);
    }

    /**
     * 确认要选择的英雄
     * @param channelId 玩家标识
     * @param heroId 选择的英雄ID
     * @return {@code boolean}
     */
    public boolean confirmHero(String channelId, Integer heroId) {
        MatchPlayer player = players.get(channelId);
        if (player != null) {
            player.setSelectHero(true);
            player.setHeroId(heroId);
            return checkAllHero();
        }

        return false;
    }

    /**
     * 检查玩家是否全部选择好了英雄
     * @return {@code boolean}
     */
    private boolean checkAllHero() {
        final ReentrantLock lock1 = this.lock;
        lock1.lock();
        try {
            return players.values().stream().allMatch(MatchPlayer::isSelectHero);
        }  finally {
            lock1.unlock();
        }
    }

    /**
     * 回收资源、防止内存溢出
     */
    private void clearAll() {
        players.clear();
        LocalCacheUtil.ACTIVE_ROOM.remove(roomId);
    }
}
